############################################################################################
print('\n第18部分')
'''
函数

输入
函数处理
输出

先定义，后调用
不调用，不执行
调用一次，执行一次

形参与实参
有参函数
无参函数
'''

def fun(a, b):  # 形参
    '''有参数函数'''
    c = a + b
    print(f'{a}+{b}={c}')
    return c  # 函数结果返回

a = 1
b = 2
c = fun(2, 3)  # 实参
print('得到函数处理好的返回结果：',c)


def fun1():  # 无参数
    '''
    无参数函数
    '''
    print('test')


############################################################################################
print('\n第19部分')
'''
可变数据类型的参数传递     可变
不可变数据类型的参数传递
'''

#  可变数据类型的参数传递  列表、字典 自定义一些可变对象
b = [10,20]
print(id(b))


def fun2(m):
    '''可变数据类型的参数传递'''
    print('m:',m,id(m))
    m.append(30)


fun2(b)  # m: [10, 20] 2405940421440
print(b, id(b))  # [10, 20, 30] 2405940421440

'''
我们所有赋值的操作，其实都是引用的赋值 引用传递 而不是值传递
'''

#  不可变数据类型的参数传递  列表、字典 自定义一些可变对象
num = 100
print(num, id(num))  # 100 140709794602888


def fun3(n):
    print('n:', n, id(n))  # n: 100 140709794602888
    # 对不可变对象进行操作
    n = n + 1  # 由于不可变对象不可以被修改，随意系统会生成一个新的对象
    print('n:', n, id(n))  # n: 101 140709794602920


fun3(num)  # n: 101 140709794602920
print(num, id(num))  # 100 140709794602888


############################################################################################
print('\n第20部分')
'''
深拷贝与浅拷贝
浅拷贝 不拷贝对象的内容，只是拷贝【原对象】里面的【子对象】的【引用】
拷贝的里面有可变类型数据，如果【新对象】进行更改，会造成【原对象】一起进行更改
'''
import copy
a = [1, 2, [3, 4], 5]
b = copy.copy(a)

print(a, id(a), id(a[2]))  # [1, 2, [3, 4], 5] 2897221812480 2897221867904
print(b, id(b), id(b[2]))  # [1, 2, [3, 4], 5] 2897221861568 2897221867904

b.append(6)
print(a, id(a), id(a[2]))  # [1, 2, [3, 4], 5] 2897221812480 2897221867904
print(b, id(b), id(b[2]))  # [1, 2, [3, 4], 5, 6] 2897221861568 2897221867904

b[2].append(6)
print(a, id(a), id(a[2]))  # [1, 2, [3, 4, 6], 5] 2897221812480 2897221867904
print(b, id(b), id(b[2]))  # [1, 2, [3, 4, 6], 5, 6] 2897221861568 2897221867904

# a与b是两个对象地址不同，列表中1，2，5三个是不可变的int类型，不受影响，
# ！！！【但[3, 4]是列表，在a与b中只是列表的引用，是个地址，b改变[3, 4]也会影响a的[3, 4]】

'''
深拷贝 拷贝对象的内容，【新对象】与【旧的对象】【完全独立】
'''
a = [1, 2, [3, 4], 5]
b = copy.deepcopy(a)

print(a, id(a), id(a[2]))  # [1, 2, [3, 4], 5] 2405940307072 2405940403648
print(b, id(b), id(b[2]))  # [1, 2, [3, 4], 5] 2405940334592 2405940414848

b.append(6)
print(a, id(a), id(a[2]))  # [1, 2, [3, 4], 5] 2405940307072 2405940403648
print(b, id(b), id(b[2]))  # [1, 2, [3, 4], 5, 6] 2405940334592 2405940414848

b[2].append(6)
print(a, id(a), id(a[2]))  # [1, 2, [3, 4], 5] 2405940307072 2405940403648
print(b, id(b), id(b[2]))  # [1, 2, [3, 4, 6], 5, 6] 2405940334592 2405940414848


############################################################################################
print('\n第21部分')
'''
全局变量与局部变量
'''
a = 1  # a是全局变量
def fun4():
    b = 2  # b是局部变量,在函数作用域可以使用
    print(b)  # 2
    global a  # 【全局变量】【声明一下】，才可以在【函数作用域】使用
    print(a)  # 1
    a = 3  #
    print(a)  # 3
    print(locals())  # 局部变量{'b': 2}
    print(globals())  # 全局变量


fun4()
print(a)  # 3


############################################################################################
print('\n第22部分')
'''
参数的类型
'''
# 1、位置参数
def fun5(a,b,c):
    print(a,b,c)
fun5(1,2,3)  # 输出结果：1 2 3
# fun5(1,2,3,4) # Traceback (most recent call last):    #参数数量对不上或类型错误就会报错

# 2、默认值参数
def fun6(a,b,c=100):
    print(a,b,c)
fun6(2,3)  # 2 3 100

# 3、命名参数

def fun5(a,b,c):
    print(a+b+c)
fun5(1,2,3)  # 把【之前的fun5函数】给【覆盖】了，输出结果：6

# 4、可变参数，不定长参数 *参数
def fun6(a, *b):
    print(a)
    print(b)
fun6(1,2,[3,4],5)  # 输出a=1 b=(2, [3, 4], 5) 【第一个是a的值，后续的都是元组b的数据】

# 5、可变参数，不定长参数 **参数
def fun7(a, *b, **c):
    print(a)
    print(b)
    print(c)
fun7(1,2,[3,4],5, name='tom', age=18)  # 输出c的值是字典{'c': 'tom', 'age': 18}


# 6、强制命名参数
def fun8(*a,b,c):
    print(a,b,c)
fun8(2,b=200,c=100)  # b是200，c是100，剩下2没人要就给【*a】了


############################################################################################
print('\n第23部分')
'''
匿名函数 lambda 参数一，参数二：返回值
'''
d = lambda x,y: x*y
print(d(3,4))

d = lambda x,y: x if x > y else y


############################################################################################
print('\n第24部分')
'''
闭包：1、避免代码冗余；2、避免变量污染
函数内部使用嵌套函数（加上内部函数） 第一个特点：有嵌套
有引用
有返回值

内部函数使用了外部函数的变量，且外部函数返回了内部函数，我们把这个使用外部函数变量的内部函数称之为闭包
'''
def outer(a):
    print('外部函数')
    def inner(b):
        print('内部函数')
        return a+b
    return inner
print(outer(10)(7))
print(outer(2)(3))


# 内部函数调用外部函数c，可以避免数据污染
def outer():
    c = 0
    def inner():
        nonlocal c
        c += 1
        return c
    return inner
print(outer()())
print(outer()())


############################################################################################
print('\n第25部分')
'''
表达式求值函数 eval
'''

a = 1
b = 2
c = 'a+b'
print(eval(c))  # 把字符串当一个语句执行


############################################################################################
print('\n第26部分')
'''
递归
函数自己调用自己
'''


def my_re(n):
    if n == 1:  # 终止条件
        return 1
    else:
        print(f'{n}*{n - 1}={n*(n-1)}')
        return n * my_re(n-1)  # 调用的地方


print(my_re(5))  # 120
print(1*2*3*4*5)  # 120

def my_re(n):
    # 求 1/2+2/3+3/4+...+n/(n+1)
    if n == 1:
        return 0.5
    else:
        t = float(n)/(float(n)+1)
        print(f'{n}/{n+1}={t}')
        return my_re(n-1) + t
my_re(5)  # 3.5500000000000003



